/**
 * Copyright 2011 Adrian Witas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.abstractmeta.commandify.core.command.collection;

import org.abstractmeta.commandify.command.Commands;
import org.abstractmeta.commandify.core.command.AbstractCommand;
import org.abstractmeta.commandify.core.command.AbstractConstructorCommand;
import org.abstractmeta.commandify.core.command.AbstractMethodCommand;
import org.abstractmeta.commandify.core.command.CommandifyCollection;

import java.util.*;

/**
 * Represents a list which implement commandify.
 * This enables all mutable collection method calls being encapsulated as method commands.
 * If collection value implements commandify then as long as it is possible to build cloned version build, it will be used
 * instead of actual value.
 */
public class CommandifyList<T> extends CommandifyCollection<T> implements List<T> {

    protected final List<T> list;

    public CommandifyList(List<T> list)  {
        super(List.class, list);
        this.list = list;
    }

    @SuppressWarnings("unchecked")
    protected void addConstructorCommand() {
        Commands commands = getCommands();
        commands.setConstructorCommand(new AbstractConstructorCommand<List<T>>() {
            @Override
            public List<T> execute() {
                return new ArrayList<T>();
            }
        });
    }


    @Override
    @SuppressWarnings("unchecked")
    public boolean addAll(final int i, final Collection<? extends T> collection) {
        AbstractCommand command = new AbstractMethodCommand<List<T>>("addAll", new Class[]{int.class, Collection.class}, new Object[]{i, collection}) {
            @Override
            public void execute(List<T> instance) {
                instance.addAll(i, collection);
            }

        };
        getCommands().add(command);
        return list.addAll(i, collection);
    }

    @Override
    public T get(int i) {
        return list.get(i);
    }

    @Override
    @SuppressWarnings("unchecked")
    public T set(final int i, final T t) {
        AbstractCommand command = new AbstractMethodCommand<List<T>>("set", new Class[]{int.class, t.getClass()}, new Object[]{i, t}) {
            @Override
            public void execute(List<T> instance) {
                instance.set(i, getCommandValue(t));
            }

        };
        getCommands().add(command);
        return list.set(i, t);
    }

    @Override
    @SuppressWarnings("unchecked")
    public void add(final int i, final T t) {
        AbstractCommand command = new AbstractMethodCommand<List<T>>("add", new Class[]{int.class, t.getClass()}, new Object[]{i, t}) {
            @Override
            public void execute(List<T> instance) {
                instance.add(i, getCommandValue(t));
            }

        };
        getCommands().add(command);
        list.add(i, t);
    }

    @Override
    @SuppressWarnings("unchecked")
    public T remove(final int i) {
        AbstractCommand command = new AbstractMethodCommand<List<T>>("remove", new Class[]{int.class}, new Object[]{i}) {
            @Override
            public void execute(List<T> instance) {
                instance.remove(i);
            }

        };
        getCommands().add(command);
        return list.remove(i);
    }

    @Override
    public int indexOf(Object o) {
        return list.indexOf(o);
    }

    @Override
    public int lastIndexOf(Object o) {
        return list.lastIndexOf(o);
    }

    @Override
    public ListIterator<T> listIterator() {
        return list.listIterator();
    }

    @Override
    public ListIterator<T> listIterator(int i) {
        return list.listIterator(i);
    }

    @Override
    public List<T> subList(int i, int i2) {
        return list.subList(i, i2);
    }
}
